﻿// -----------------------------------------------------------------------
// <copyright file="Filters.cs" company="Microsoft">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace ImageP
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    /// <summary>
    /// structure for each pixel represented in red, green, blue colors in double.
    /// </summary>
    public struct RGBCOLORS
    {
        /// <summary>
        /// the red value
        /// </summary>
        public double R;

        /// <summary>
        /// the green value
        /// </summary>
        public double G;

        /// <summary>
        /// the blue value
        /// </summary>
        public double B;
    }

    /// <summary>
    /// Class Filters
    /// </summary>
    public class Filters
    {
        /// <summary>
        /// An array for horizontal right edge-detection and sharpening
        /// </summary>
        private double[,] horizontal = new double[3, 3];

        /// <summary>
        /// An array for La placian edge-detection and sharpening
        /// </summary>
        private double[,] theLaPlacain = new double[3, 3];

        /// <summary>
        /// An array for vertical edge-detection and sharpening
        /// </summary>
        private double[,] vertical = new double[3, 3];

        /// <summary>
        /// An array for diagonal left edge-detection and sharpening
        /// </summary>
        private double[,] diagonalLeft = new double[3, 3];

        /// <summary>
        /// An array for diagonal right edge-detection and sharpening
        /// </summary>
        private double[,] diagonalRight = new double[3, 3];

        /// <summary>
        /// The buffer after normalization
        /// </summary>
        private RGBCOLORS[,] normalizedBuffer;

        /// <summary>
        /// An object from the sturct for the old image
        /// </summary>
        private RGBcolor[,] oldImage;

        /// <summary>
        /// A bitmap that contains the old image.
        /// </summary>
        private Bitmap bm;

        /// <summary>
        /// The final bitmap that contains the image
        /// </summary>
        private Bitmap finalBm;

        /// <summary>
        /// object from struct to assign red,green,blue values
        /// </summary>
        private RGBCOLORS[,] temps = new RGBCOLORS[3, 3];

       /// <summary>
       /// A struct object of the final image
       /// </summary>
        private RGBCOLORS[,] finalImage;

        /// <summary>
        /// The values that have the sum of the all values in the mask after being multiplied
        /// </summary>
        private double maskSumR = 0, maskSumB = 0, maskSumG = 0;

/// <summary>
/// takes the mask array and form the convolution of the original image with this mask
/// </summary>
/// <param name="image"> The image </param>
/// <param name="mask">A 2D array of the mask value</param>
/// <param name="type">To know if its edge detection,sharpening or any other type of filters </param>
        public void Convolution(Image image, double[,] mask, string type)
        {
         
            {
                this.bm = new Bitmap(image);
                Bitmap fin = new Bitmap(Convert.ToInt32(image.Width), Convert.ToInt32(image.Height));
                this.oldImage = new RGBcolor[image.Height + 1, image.Width + 1];
                for (int i = 0; i < image.Width + 1; i++)
                {
                    this.oldImage[0, i].Red = 0;
                    this.oldImage[image.Height, i].Red = 0;

                    this.oldImage[0, i].Green = 0;
                    this.oldImage[image.Height, i].Green = 0;

                    this.oldImage[0, i].Blue = 0;
                    this.oldImage[image.Height, i].Blue = 0;
                }

                for (int i = 0; i < image.Height + 1; i++)
                {
                    this.oldImage[i, 0].Red = 0;
                    this.oldImage[i, image.Width].Red = 0;

                    this.oldImage[i, 0].Green = 0;
                    this.oldImage[i, image.Width].Green = 0;

                    this.oldImage[i, 0].Blue = 0;
                    this.oldImage[i, image.Width].Blue = 0;
                }

                for (int i = 1; i <= image.Height; i++)
                {
                    for (int j = 1; j <= image.Width; j++)
                    {
                        this.oldImage[i, j].Red = this.bm.GetPixel(j - 1, i - 1).R;
                        this.oldImage[i, j].Green = this.bm.GetPixel(j - 1, i - 1).G;
                        this.oldImage[i, j].Blue = this.bm.GetPixel(j - 1, i - 1).B;
                    }
                }

                int counterI = 0, counterJ = 0;

                this.finalImage = new RGBCOLORS[image.Height + 1, image.Width + 1];
                this.temps = new RGBCOLORS[mask.GetLength(0), mask.GetLength(0)];
                for (int i = 0; i < image.Height; i++)
                {
                    for (int j = 0; j < image.Width; j++)
                    {
                        for (int h = i; h <= (i + mask.GetLength(0) - 1); h++)
                        {
                            Color cl;
                            if (h == image.Height)
                            {
                                break;
                            }

                            for (int g = j; g <= (j + mask.GetLength(0) - 1); g++)
                            {
                                cl = fin.GetPixel(j, i);
                                if (g == image.Width)
                                {
                                    break;
                                }

                                this.temps[counterI, counterJ].R = mask[counterI, counterJ] * this.oldImage[h, g].Red;

                                this.temps[counterI, counterJ].G = mask[counterI, counterJ] * this.oldImage[h, g].Green;

                                this.temps[counterI, counterJ].B = mask[counterI, counterJ] * this.oldImage[h, g].Blue;
                                counterJ++;
                            }

                            counterI++;
                            counterJ = 0;
                        }

                        counterI = 0;
                        counterJ = 0;
                        for (int x = 0; x < mask.GetLength(0); x++)
                        {
                            for (int y = 0; y < mask.GetLength(0); y++)
                            {
                                this.maskSumR += this.temps[x, y].R;

                                this.maskSumG += this.temps[x, y].G;

                                this.maskSumB += this.temps[x, y].B;
                            }
                        }

                        this.finalImage[i, j].R = this.maskSumR;
                        this.finalImage[i, j].G = this.maskSumG;
                        this.finalImage[i, j].B = this.maskSumB;
                        this.maskSumR = 0;
                        this.maskSumG = 0;
                        this.maskSumB = 0;
                    }
                }

                if (type == "edgeDetect")
                {
                    this.normalizedBuffer = new RGBCOLORS[image.Height + 1, image.Width + 1];
                    Normalize normalize = new Normalize();
                    this.normalizedBuffer = normalize.Normalize_image(this.finalImage, image.Height + 1, image.Width + 1);
                    this.finalBm = new Bitmap(image.Width + 1, image.Height + 1);

                    for (int i = 0; i <= image.Height; i++)
                    {
                        for (int j = 0; j <= image.Width; j++)
                        {
                            this.finalBm.SetPixel(j, i, System.Drawing.Color.FromArgb(Convert.ToInt32(this.normalizedBuffer[i, j].R), Convert.ToInt32(this.normalizedBuffer[i, j].G), Convert.ToInt32(this.normalizedBuffer[i, j].B)));
                        }
                    }
                }
                else if (type == "sharpening")
                {
                    this.normalizedBuffer = new RGBCOLORS[image.Height + 1, image.Width + 1];
                    this.finalBm = new Bitmap(image.Width + 1, image.Height + 1);
                    for (int i = 0; i < image.Height; i++)
                    {
                        for (int j = 0; j < image.Width; j++)
                        {
                            if (this.finalImage[i, j].R >= 255)
                            {
                                this.finalImage[i, j].R = 255;
                            }
                            else if (this.finalImage[i, j].R <= 0)
                            {
                                this.finalImage[i, j].R = 0;
                            }

                            if (this.finalImage[i, j].G >= 255)
                            {
                                this.finalImage[i, j].G = 255;
                            }
                            else if (this.finalImage[i, j].G <= 0)
                            {
                                this.finalImage[i, j].G = 0;
                            }

                            if (this.finalImage[i, j].B >= 255)
                            {
                                this.finalImage[i, j].B = 255;
                            }
                            else if (this.finalImage[i, j].B <= 0)
                            {
                                this.finalImage[i, j].B = 0;
                            }
                        }
                    }

                    for (int i = 0; i <= image.Height; i++)
                    {
                        for (int j = 0; j <= image.Width; j++)
                        {
                            this.finalBm.SetPixel(j, i, System.Drawing.Color.FromArgb(Convert.ToInt32(this.finalImage[i, j].R), Convert.ToInt32(this.finalImage[i, j].G), Convert.ToInt32(this.finalImage[i, j].B)));
                        }
                    }
                }
                else if (type == "none")
                {
                    this.finalBm = new Bitmap(image.Width + 1, image.Height + 1);
                    for (int i = 0; i <= image.Height; i++)
                    {
                        for (int j = 0; j <= image.Width; j++)
                        {
                            this.finalBm.SetPixel(j, i, System.Drawing.Color.FromArgb(Convert.ToInt32(this.finalImage[i, j].R), Convert.ToInt32(this.finalImage[i, j].G), Convert.ToInt32(this.finalImage[i, j].B)));
                        }
                    }
                }
            }
            
        }

        /// <summary>
        /// Horizontal edge-detection
        /// </summary>
        /// <param name="image">The image to form the edge detection on</param>
        /// <returns>The new image after edge-detection</returns>
        public Bitmap HorizontalEdgeDetection(Image image)
        {
            this.horizontal[0, 0] = 5;
            this.horizontal[0, 1] = 5;
            this.horizontal[0, 2] = 5;

            this.horizontal[1, 0] = -3;
            this.horizontal[1, 1] = 0;
            this.horizontal[1, 2] = -3;

            this.horizontal[2, 0] = -3;
            this.horizontal[2, 1] = -3;
            this.horizontal[2, 2] = -3;
            this.Convolution(image, this.horizontal, "edgeDetect");
            return this.finalBm;
        }

        /// <summary>
        /// Vertical edge-detection
        /// </summary>
        /// <param name="image">The image to form the edge detection on</param>
        /// <returns>The new image after Vertical edge-detection</returns>
        public Bitmap VerticalEdgeDetection(Image image)
        {
            this.vertical[0, 0] = 5;
            this.vertical[0, 1] = -3;
            this.vertical[0, 2] = -3;

            this.vertical[1, 0] = 5;
            this.vertical[1, 1] = 0;
            this.vertical[1, 2] = -3;

            this.vertical[2, 0] = 5;
            this.vertical[2, 1] = -3;
            this.vertical[2, 2] = -3;

            this.Convolution(image, this.vertical, "edgeDetect");
            return this.finalBm;
        }

        /// <summary>
        /// Diagonal left edge-detection
        /// </summary>
        /// <param name="image">The image to form the edge detection on</param>
        /// <returns>The new image after Diagonal left edge-detection</returns>
        public Bitmap DiagonalLeftEdgeDetection(Image image)
        {
            this.diagonalLeft[0, 0] = -3;
            this.diagonalLeft[0, 1] = 5;
            this.diagonalLeft[0, 2] = 5;

            this.diagonalLeft[1, 0] = -3;
            this.diagonalLeft[1, 1] = 0;
            this.diagonalLeft[1, 2] = 5;

            this.diagonalLeft[2, 0] = -3;
            this.diagonalLeft[2, 1] = -3;
            this.diagonalLeft[2, 2] = -3;

            this.Convolution(image, this.diagonalLeft, "edgeDetect");
            return this.finalBm;
        }

        /// <summary>
        /// Diagonal right edge-detection
        /// </summary>
        /// <param name="image">The image to form the edge detection on</param>
        /// <returns>The new image after edge-detection</returns>
        public Bitmap DiagonalRightEdgeDetection(Image image)
        {
            this.diagonalRight[0, 0] = 5;
            this.diagonalRight[0, 1] = 5;
            this.diagonalRight[0, 2] = -3;

            this.diagonalRight[1, 0] = 5;
            this.diagonalRight[1, 1] = 0;
            this.diagonalRight[1, 2] = -3;

            this.diagonalRight[2, 0] = -3;
            this.diagonalRight[2, 1] = -3;
            this.diagonalRight[2, 2] = -3;

            this.Convolution(image, this.diagonalRight, "edgeDetect");
            return this.finalBm;
        }

        /// <summary>
        /// Laplace edge-detection
        /// </summary>
        /// <param name="image">The image to form the edge detection on</param>
        /// <returns>The new image after edge-detection</returns>
        public Bitmap LaPlaceEdgeDetection(Image image)
        {
            this.theLaPlacain[0, 0] = -1;
            this.theLaPlacain[0, 1] = -1;
            this.theLaPlacain[0, 2] = -1;

            this.theLaPlacain[1, 0] = -1;
            this.theLaPlacain[1, 1] = 8;
            this.theLaPlacain[1, 2] = -1;

            this.theLaPlacain[2, 0] = -1;
            this.theLaPlacain[2, 1] = -1;
            this.theLaPlacain[2, 2] = -1;
            this.Convolution(image, this.theLaPlacain, "edgeDetect");
            return this.finalBm;
        }

        /// <summary>
        /// Laplacian Sharpening
        /// </summary>
        /// <param name="image">The image to form the Sharpening on</param>
        /// <returns>The new image after Sharpening</returns>
        public Bitmap LaplaceSharpening(Image image)
        {
            this.theLaPlacain[0, 0] = 0;
            this.theLaPlacain[0, 1] = -1;
            this.theLaPlacain[0, 2] = 0;

            this.theLaPlacain[1, 0] = -1;
            this.theLaPlacain[1, 1] = 5;
            this.theLaPlacain[1, 2] = -1;

            this.theLaPlacain[2, 0] = 0;
            this.theLaPlacain[2, 1] = -1;
            this.theLaPlacain[2, 2] = 0;
            this.Convolution(image, this.theLaPlacain, "sharpening");
            return this.finalBm;
        }

        /// <summary>
        /// Horizontal Sharpening
        /// </summary>
        /// <param name="image">The image to form the Sharpening on</param>
        /// <returns>The new image after Sharpening</returns>
        public Bitmap HorizontalSharpening(Image image)
        {
            this.horizontal[0, 0] = 0;
            this.horizontal[0, 1] = 1;
            this.horizontal[0, 2] = 0;

            this.horizontal[1, 0] = 0;
            this.horizontal[1, 1] = 1;
            this.horizontal[1, 2] = 0;

            this.horizontal[2, 0] = 0;
            this.horizontal[2, 1] = -1;
            this.horizontal[2, 2] = 0;
            this.Convolution(image, this.horizontal, "sharpening");
            return this.finalBm;
        }

        /// <summary>
        /// Vertical Sharpening
        /// </summary>
        /// <param name="image">The image to form the Sharpening on</param>
        /// <returns>The new image after Sharpening</returns>
        public Bitmap VerticalSharpening(Image image)
        {
            this.vertical[0, 0] = 0;
            this.vertical[0, 1] = 0;
            this.vertical[0, 2] = 0;

            this.vertical[1, 0] = 1;
            this.vertical[1, 1] = 1;
            this.vertical[1, 2] = -1;

            this.vertical[2, 0] = 0;
            this.vertical[2, 1] = 0;
            this.vertical[2, 2] = 0;
            this.Convolution(image, this.vertical, "sharpening");
            return this.finalBm;
        }

        /// <summary>
        /// Diagonal right Sharpening
        /// </summary>
        /// <param name="image">The image to form the Sharpening on</param>
        /// <returns>The new image after Sharpening</returns>
        public Bitmap DiagonalRightSharpening(Image image)
        {
            this.diagonalRight[0, 0] = 1;
            this.diagonalRight[0, 1] = 0;
            this.diagonalRight[0, 2] = 0;

            this.diagonalRight[1, 0] = 0;
            this.diagonalRight[1, 1] = 1;
            this.diagonalRight[1, 2] = 0;

            this.diagonalRight[2, 0] = 0;
            this.diagonalRight[2, 1] = 0;
            this.diagonalRight[2, 2] = -1;

            this.Convolution(image, this.diagonalRight, "sharpening");
            return this.finalBm;
        }

        /// <summary>
        /// Diagonal left Sharpening
        /// </summary>
        /// <param name="image">The image to form the Sharpening on</param>
        /// <returns>The new image after Sharpening</returns>
        public Bitmap DiagonalLeftSharpening(Image image)
        {
            this.diagonalLeft[0, 0] = 0;
            this.diagonalLeft[0, 1] = 0;
            this.diagonalLeft[0, 2] = 1;

            this.diagonalLeft[1, 0] = 0;
            this.diagonalLeft[1, 1] = 1;
            this.diagonalLeft[1, 2] = 0;

            this.diagonalLeft[2, 0] = -1;
            this.diagonalLeft[2, 1] = 0;
            this.diagonalLeft[2, 2] = 0;

            this.Convolution(image, this.diagonalLeft, "sharpening");
            return this.finalBm;
        }

     /// <summary>
     /// Gaussian Filter
     /// </summary>
     /// <param name="image">The image to apply the filter on</param>
     /// <param name="sigma">The sigma value</param>
     /// <returns>The new image after applying the filter</returns>
        public Bitmap GaussianFilter(Image image, int sigma)
        {
            int n = Convert.ToInt32((3.7 * sigma) - 0.5);
            int k = (2 * n) + 1;
            double[,] gaussianMask = new double[k, k];
            int x = -k / 2;
            int y = -k / 2;

            for (int i = 0; i < k; i++)
            {
                x = -k / 2;

                for (int j = 0; j < k; j++)
                {
                    double val = Math.Pow(x, 2);
                    gaussianMask[i, j] = Convert.ToDouble(1 / (2 * Math.PI * Convert.ToDouble(Math.Pow(sigma, 2)))) * Convert.ToDouble(Math.Exp(-Convert.ToDouble(Math.Pow(x, 2) + Math.Pow(y, 2)) / Convert.ToDouble(2 * Math.Pow(sigma, 2))));

                    x++;
                }

                y++;
            }

            this.Convolution(image, gaussianMask, "none");
            return this.finalBm;
        }

       /// <summary>
       /// Mean Filter
       /// </summary>
       /// <param name="image">The image to apply the filter on</param>
       /// <param name="maskSize">Mask Size</param>
       /// <returns>The new image after applying the filter</returns>
        public Bitmap MeanFilter(Image image, int maskSize)
        {
            double[,] meanMask = new double[maskSize, maskSize];
            double mask2 = Convert.ToDouble(Math.Pow(maskSize, 2));

            for (int i = 0; i < maskSize; i++)
            {
                for (int j = 0; j < maskSize; j++)
                {
                    meanMask[i, j] = 1 / mask2;
                }
            }

            this.Convolution(image, meanMask, "none");
            return this.finalBm;
        }
    }
}